using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using System.Xml;
using System.IO;
using System.ServiceModel;
using System.Runtime.Serialization;
using System.Diagnostics;

namespace Spike.ContractBuilder
{
    public class SerializationHelper<T> where T : class
    {
        public static T Deserialize(string serializedObject)
        {
            return Deserialize(serializedObject, null);
        }
        public static T Deserialize(string serializedObject, Type[] extraTypes)
        {
            // TODO: is there a way to identify what type of serialization to use ahead of time?            
            T value = null;
            try
            {
                StringReader reader = new StringReader(serializedObject);
                XmlSerializer serializer = extraTypes == null ? new XmlSerializer(typeof(T)) : new XmlSerializer(typeof(T), extraTypes);
                value = serializer.Deserialize(reader) as T;
            }
            catch (InvalidOperationException)
            {
                // handled...
            }

            if (value == null)
            {
                XmlTextReader xtr = new XmlTextReader(new StringReader(serializedObject));
                DataContractSerializer serializer = new DataContractSerializer(typeof(T));
                value = serializer.ReadObject(xtr) as T;
            }

            return value;
        }

        public static T DeserializeFromFile(string filename)
        {
            T o = null;

            XmlSerializer serializer = new XmlSerializer(typeof(T));
            using (XmlReader reader = XmlReader.Create(filename))
            {
                o = serializer.Deserialize(reader) as T;
                reader.Close();
            }
            

            return o;
        }

        public static string SerializeServiceObject(T o)
        {
            return SerializeServiceObject(o, null);
        }

        public static string SerializeServiceObject(T o, Type[] extraTypes)
        {
            StringBuilder sb = new StringBuilder();

            if (o is Exception)
            {
                FaultException fault = o as FaultException;

                if (fault == null)
                {
                    Exception ex = o as Exception;
                    sb.Append(ex.Message);
                }
                else
                {
                    try
                    {
                        XmlSerializer serializer = new XmlSerializer(typeof(FaultException));
                        using (StringWriter writer = new StringWriter(sb))
                        {
                            serializer.Serialize(writer, fault);
                        } 
                    }
                    catch
                    {
                        sb.Append(o.ToString());
                    }
                }
            }
            else
            {
                // determine if DataContract
                Type x = typeof(T);
                object[] attributes = x.GetCustomAttributes(typeof(System.Runtime.Serialization.DataContractAttribute), true);
                if (attributes.Length == 1)
                {
                    System.Runtime.Serialization.DataContractSerializer serializer = new DataContractSerializer(typeof(T), extraTypes);
                    XmlWriterSettings settings = new XmlWriterSettings();
                    settings.Indent = true;
                    settings.OmitXmlDeclaration = true;

                    using (XmlWriter writer = XmlTextWriter.Create(sb, settings))
                    {
                        serializer.WriteObject(writer, o);
                        writer.Close();
                    }
                }
                else
                {
                    XmlSerializer serializer = extraTypes == null ? new XmlSerializer(typeof(T)) : new XmlSerializer(typeof(T), extraTypes);
                    XmlWriterSettings settings = new XmlWriterSettings();
                    settings.Indent = true;
                    settings.OmitXmlDeclaration = true;

                    using (XmlWriter writer = XmlTextWriter.Create(sb, settings))
                    {
                        serializer.Serialize(writer, o);
                        writer.Close();
                    }
                }
                
            }
            return sb.ToString();
        }

        public static void SerializeServiceObjectToFile(T o, string filename)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            using (XmlWriter writer = XmlWriter.Create(filename))
            {
                serializer.Serialize(writer, o);
                writer.Close();
            }
        }
    }

    public static class SerializationHelper
    {
        private static int MAX_OBJECT_SIZE = 30000;

        /// <summary>
        /// Serialize parameter
        /// </summary>
        /// <param name="o">public serializable object</param>
        /// <returns></returns>
        public static string SerializeServiceObject(object o)
        {
            StringBuilder sb = new StringBuilder();

            try
            {
                #region If an Exception
                if (o is Exception)
                {
                    FaultException fault = o as FaultException;

                    if (fault == null)
                    {
                        Exception ex = o as Exception;
                        sb.Append(ex.Message);
                    }
                    else
                    {
                        try
                        {
                            XmlSerializer serializer = new XmlSerializer(typeof(FaultException));
                            using (StringWriter writer = new StringWriter(sb))
                            {
                                serializer.Serialize(writer, fault);
                            }
                        }
                        catch
                        {
                            sb.Append(o.ToString());
                        }
                    }
                }
                #endregion
                else
                {
                    XmlSerializer serializer = new XmlSerializer(o.GetType());
                    using (StringWriter writer = new StringWriter(sb))
                    {
                        serializer.Serialize(writer, o);
                    }
                }
            }
            catch (Exception ex)
            {
                sb.Append("Unable to SerializeServiceObject: ");
                sb.Append(ex.Message);
            }

            // of the length exceeds maximum size then truncate; this will cause some tracewriters to fail (EventLog)
            if (sb.Length > MAX_OBJECT_SIZE)
            {
                Trace.TraceWarning("Object size exceeded maximum Extended Properties size.  String truncated.");
                sb.Length = MAX_OBJECT_SIZE;
            }

            return sb.ToString();
        }
    }
}
